package gui.manageplugins;

import gui.Factory;
import java.io.IOException;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.File;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.tree.*;
import kernel.Module;
import kernel.Plugin;
import kernel.XMLPluginConfiguration;

public class MyJTree extends JTree {

    private TreeTransferHandler tth;

    public MyJTree() {
        super();
        custom();
        this.addKeyListener(new KeyListener() {

            @Override
            public void keyTyped(KeyEvent e) {
            }

            @Override
            public void keyPressed(KeyEvent e) {
            }

            @Override
            public void keyReleased(KeyEvent e) {
                if (e.getKeyCode() == 127) {
                    removeKeyAction();
                }
            }
        });
    }

    private void removeKeyAction() {
        TreePath tree = this.getSelectionPath();
        if (tree.getPathCount() != 3 && tree.getPathCount() != 2) {
            return;
        }
        if (tree.getPathCount() == 2) {
            XMLPluginConfiguration xml = new XMLPluginConfiguration(Factory.getWorkspace() +
                    File.separator + "files" + File.separator + "language.xml");
            String lng = xml.getDoc().getRootElement().getAttributeValue("selected");
            String text = xml.getTag("/languages/" + lng + "/windows/manage_plugins/delete_module_message");
            int action = javax.swing.JOptionPane.showConfirmDialog(null, text, "",
                    javax.swing.JOptionPane.YES_NO_OPTION);

            if (action == javax.swing.JOptionPane.YES_OPTION) {
                kernel.PluginOperations.removeModule(tree.getLastPathComponent().toString());
            }
            return;
        }

        String pluginName = tree.getLastPathComponent().toString();
        String moduleName = tree.getParentPath().getLastPathComponent().toString();
        kernel.PluginOperations.removePlugin(moduleName, pluginName);
        gui.Factory.makeGUISingleton().loadPluginsIcons();

        DefaultTreeModel model = (DefaultTreeModel) this.getModel();
        TreeNode[] nodes = model.getPathToRoot((TreeNode) tree.getLastPathComponent());
        model.removeNodeFromParent((DefaultMutableTreeNode) nodes[2]);
    }

    private void custom() {

        this.setDragEnabled(true);
        this.setDropMode(DropMode.ON_OR_INSERT);
        tth = new TreeTransferHandler();
        this.setTransferHandler(tth);
        this.getSelectionModel().setSelectionMode(
                TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
        expandTree(this);
    }

    /**
     * Elimina el contenido del arbol.
     *
     */
    public void reset() {
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) this.getModel().getRoot();
        DefaultMutableTreeNode newRoot = new DefaultMutableTreeNode(root.toString());
        ((DefaultTreeModel) this.getModel()).setRoot(newRoot);
    }

    private void expandTree(JTree tree) {
        DefaultMutableTreeNode root =
                (DefaultMutableTreeNode) tree.getModel().getRoot();
        Enumeration e = root.breadthFirstEnumeration();
        while (e.hasMoreElements()) {
            DefaultMutableTreeNode node =
                    (DefaultMutableTreeNode) e.nextElement();
            if (node.isLeaf()) {
                continue;
            }
            int row = tree.getRowForPath(new TreePath(node.getPath()));
            tree.expandRow(row);
        }
    }

    public void reloadModules() {
        Vector<Module> modules = kernel.PluginOperations.getAllModules();
        setModules(modules);
        this.expandTree(this);
    }

    private void setModules(Vector<Module> modules) {
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("RolGPS");
        for (Module module : modules) {
            DefaultMutableTreeNode mod = new DefaultMutableTreeNode(module.getName());
            for (Plugin plugin : module.getChildren()) {
                DefaultMutableTreeNode plug = new DefaultMutableTreeNode(plugin.getName());
                mod.add(plug);
            }
            root.add(mod);
        }
        DefaultTreeModel model = new DefaultTreeModel(root);
        this.setModel(model);
    //((DefaultTreeModel) this.getModel()).setRoot(root);

    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        MyJTree tree = new MyJTree();
        JScrollPane scroll = new JScrollPane();

        scroll.setViewportView(tree);
        f.add(scroll);

        f.setSize(400, 400);
        f.setLocation(200, 200);
        f.pack();
        f.setVisible(true);
    }
}

class TreeTransferHandler extends TransferHandler {

    DataFlavor nodesFlavor;
    DataFlavor[] flavors = new DataFlavor[1];
    DefaultMutableTreeNode[] nodesToRemove;
    private boolean copying = false;
    DefaultMutableTreeNode nodeDest;
    private boolean destSameMod;
    private String pluginMoved;


    public TreeTransferHandler() {
        try {
            String mimeType = DataFlavor.javaJVMLocalObjectMimeType +
                    ";class=\"" +
                    javax.swing.tree.DefaultMutableTreeNode[].class.getName() +
                    "\"";
            nodesFlavor = new DataFlavor(mimeType);
            flavors[0] = nodesFlavor;
        } catch (ClassNotFoundException e) {
            System.out.println("ClassNotFound: " + e.getMessage());
        }
    //add click action listener


    }

    public void setCopyAction(boolean action) {
        copying = action;
    }

    public boolean canImport(TransferHandler.TransferSupport support) {
        if (!support.isDrop()) {
            return false;
        }

        support.setShowDropLocation(true);
        if (!support.isDataFlavorSupported(nodesFlavor)) {
            return false;
        }
        // Do not allow a drop on the drag source selections.

        JTree.DropLocation dl =
                (JTree.DropLocation) support.getDropLocation();
        JTree tree = (JTree) support.getComponent();
        int dropRow = tree.getRowForPath(dl.getPath());
        int[] selRows = tree.getSelectionRows();

        //si el destino es del nivel 1 (nivel de modulo) no permitirlo
        if (dl == null || dl.getPath() == null || dl.getPath().getPathCount() == 1) {
            return false;
        }

        if(tree.getSelectionPath() == null)
            return false;

        //System.out.println("path count: "+dl.getPath().getPathCount());
        //no permitir que el modulo se pueda mover (nivel 2)
        if (tree.getSelectionPath().getPathCount() != 3) {
            return false;
        }

        String moduleDest = null;
        if (dl.getPath().getPathCount() == 3) {
            moduleDest = dl.getPath().getParentPath().getLastPathComponent().toString();
        } else {
            moduleDest = dl.getPath().getLastPathComponent().toString();
        }
        //System.out.println(moduleDest);
        String moduleOrig = tree.getSelectionPath().getParentPath().getLastPathComponent().toString();
        /*if(tree.getSelectionRows().length > 1){
            tree.setSelectionRow(tree.getSelectionRows()[0]);
        }*/
        //no permitir copiar un plugin a su mismo modulo
        if (moduleOrig.equals(moduleDest)) {
            //return false;
            destSameMod = true;
            pluginMoved = tree.getSelectionPath().getLastPathComponent().toString();
        }else
            destSameMod = false;
        


        for (int i = 0; i < selRows.length; i++) {
            if (selRows[i] == dropRow) {
                return false;
            }
        }
        // Do not allow MOVE-action drops if a non-leaf node is
        // selected unless all of its children are also selected.
        int action = support.getDropAction();
        if (action == MOVE) {
            return haveCompleteNode(tree);
        }
        // Do not allow a non-leaf node to be copied to a level
        // which is less than its source level.
        TreePath dest = dl.getPath();
        DefaultMutableTreeNode target =
                (DefaultMutableTreeNode) dest.getLastPathComponent();
        TreePath path = tree.getPathForRow(selRows[0]);
        DefaultMutableTreeNode firstNode =
                (DefaultMutableTreeNode) path.getLastPathComponent();
        if (firstNode.getChildCount() > 0 &&
                target.getLevel() < firstNode.getLevel()) {
            return false;
        }
        return true;
    }

    private boolean haveCompleteNode(JTree tree) {
        int[] selRows = tree.getSelectionRows();
        TreePath path = tree.getPathForRow(selRows[0]);
        DefaultMutableTreeNode first =
                (DefaultMutableTreeNode) path.getLastPathComponent();
        int childCount = first.getChildCount();
        // first has children and no children are selected.
        if (childCount > 0 && selRows.length == 1) {
            return false;
        }
        // first may have children.
        for (int i = 1; i < selRows.length; i++) {
            path = tree.getPathForRow(selRows[i]);
            DefaultMutableTreeNode next =
                    (DefaultMutableTreeNode) path.getLastPathComponent();
            if (first.isNodeChild(next)) {
                // Found a child of first.
                if (childCount > selRows.length - 1) {
                    // Not all children of first are selected.
                    return false;
                }
            }
        }
        return true;
    }

    protected Transferable createTransferable(JComponent c) {
        JTree tree = (JTree) c;
        TreePath[] paths = tree.getSelectionPaths();
        if (paths != null) {
            // Make up a node array of copies for transfer and
            // another for/of the nodes that will be removed in
            // exportDone after a successful drop.
            List<DefaultMutableTreeNode> copies =
                    new ArrayList<DefaultMutableTreeNode>();
            List<DefaultMutableTreeNode> toRemove =
                    new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode node =
                    (DefaultMutableTreeNode) paths[0].getLastPathComponent();
            DefaultMutableTreeNode copy = copy(node);
            copies.add(copy);
            toRemove.add(node);
            for (int i = 1; i < paths.length; i++) {
                DefaultMutableTreeNode next =
                        (DefaultMutableTreeNode) paths[i].getLastPathComponent();
                // Do not allow higher level nodes to be added to list.
                if (next.getLevel() < node.getLevel()) {
                    break;
                } else if (next.getLevel() > node.getLevel()) {  // child node
                    copy.add(copy(next));
                // node already contains child
                } else {                                        // sibling
                    copies.add(copy(next));
                    toRemove.add(next);
                }
            }
            DefaultMutableTreeNode[] nodes =
                    copies.toArray(new DefaultMutableTreeNode[copies.size()]);
            nodesToRemove =
                    toRemove.toArray(new DefaultMutableTreeNode[toRemove.size()]);
            return new NodesTransferable(nodes);
        }
        return null;
    }

    /** Defensive copy used in createTransferable. */
    private DefaultMutableTreeNode copy(TreeNode node) {
        return new DefaultMutableTreeNode(node);
    }

    protected void exportDone(JComponent source, Transferable data, int action) {
        if (destSameMod) {
            JTree tree = (JTree) source;
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
            //dest invalid
            if(nodeDest == null)
                return;
            
            for (int i = 0; i < nodesToRemove.length; i++) {
                model.removeNodeFromParent(nodesToRemove[i]);
            }
            //System.out.println(nodeDest);
            System.out.println("nodo moviendo: "+pluginMoved);
            DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
            int count = root.getChildCount();
            int aux = 0;
            for(int i = 0;i<count;i++){
                if(nodeDest.toString().equals(root.getChildAt(i).toString())){
                    Enumeration en = root.getChildAt(i).children();
                    
                    while(en.hasMoreElements()){
                        Object ob = en.nextElement();
                        if(ob.toString().equals(pluginMoved)){
                            break;
                        }
                        //incrementar aux
                        aux++;
                    }
                }
            }
            //System.out.println("nueva posicion: "+aux);
            kernel.PluginOperations.movePluginInsideModuleTo(pluginMoved, nodeDest.toString(), aux);
            return;
        }
        if ((action & MOVE) == MOVE) {
            JTree tree = (JTree) source;
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            // Remove nodes saved in nodesToRemove in createTransferable.
            for (int i = 0; i < nodesToRemove.length; i++) {
                try {
                    System.out.println("[MyJTree.move] plugin: " + nodesToRemove[i].toString() + " modSource: " + nodesToRemove[i].getParent().toString() + " modDest: " + nodeDest.toString());
                    kernel.PluginOperations.movePluginToModule(nodesToRemove[i].toString(),
                            nodesToRemove[i].getParent().toString(),
                            nodeDest.toString());
                    model.removeNodeFromParent(nodesToRemove[i]);
                /**/
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            /**/
            }
        } else {
            if ((action & COPY) == COPY) {
                JTree tree = (JTree) source;
                DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

                // Remove nodes saved in nodesToRemove in createTransferable.
                for (int i = 0; i < nodesToRemove.length; i++) {
                    try {
                        System.out.println("[MyJTree.copy] plugin: " + nodesToRemove[i].toString() + " modSource: " + nodesToRemove[i].getParent().toString() + " modDest: " + nodeDest.toString());
                        kernel.PluginOperations.copyPluginToModule(nodesToRemove[i].toString(),
                                nodesToRemove[i].getParent().toString(),
                                nodeDest.toString());
                    /**/
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                /**/
                }
            } else {
                System.out.println("No move or copy action");
            }
        }
    }

    public int getSourceActions(JComponent c) {
        return COPY_OR_MOVE;
    }

    public boolean importData(TransferHandler.TransferSupport support) {
        if (!canImport(support)) {
            return false;
        }

        // Extract transfer data.
        DefaultMutableTreeNode[] nodes = null;
        try {
            Transferable t = support.getTransferable();
            nodes = (DefaultMutableTreeNode[]) t.getTransferData(nodesFlavor);
        } catch (UnsupportedFlavorException ufe) {
            System.out.println("UnsupportedFlavor: " + ufe.getMessage());
        } catch (java.io.IOException ioe) {
            System.out.println("I/O error: " + ioe.getMessage());
        }
        // Get drop location info.
        JTree.DropLocation dl =
                (JTree.DropLocation) support.getDropLocation();
        int childIndex = dl.getChildIndex();
        TreePath dest = dl.getPath();
        if (dl.getPath().getPathCount() == 3) {
            dest = dl.getPath().getParentPath();
        }
        //System.out.println(dl.getPath().toString());
        DefaultMutableTreeNode parent =
                (DefaultMutableTreeNode) dest.getLastPathComponent();
        JTree tree = (JTree) support.getComponent();
        DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
        // Configure for drop mode.
        int index = childIndex;    // DropMode.INSERT
        if (childIndex == -1) {     // DropMode.ON
            index = parent.getChildCount();
        }
        nodeDest = parent;
        if(nodeDest == null)
            return false;


        // Add data to model.
        for (int i = 0; i < nodes.length; i++) {
            model.insertNodeInto(nodes[i], parent, index++);
        }
        return true;
    }

    public String toString() {
        return getClass().getName();
    }

    public class NodesTransferable implements Transferable {

        DefaultMutableTreeNode[] nodes;

        public NodesTransferable(DefaultMutableTreeNode[] nodes) {
            this.nodes = nodes;
        }

        public Object getTransferData(DataFlavor flavor)
                throws UnsupportedFlavorException {
            if (!isDataFlavorSupported(flavor)) {
                throw new UnsupportedFlavorException(flavor);
            }
            return nodes;
        }

        public DataFlavor[] getTransferDataFlavors() {
            return flavors;
        }

        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return nodesFlavor.equals(flavor);
        //return true;
        //return false;
        }
    }
}